/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007-2008 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.spring.factory;

import java.util.Properties;

import org.springframework.beans.factory.FactoryBean;

import com.genia.toolbox.basics.manager.FileManager;
import com.opensymphony.oscache.base.Cache;
import com.opensymphony.oscache.base.algorithm.LRUCache;
import com.opensymphony.oscache.general.GeneralCacheAdministrator;
import com.opensymphony.oscache.plugins.diskpersistence.HashDiskPersistenceListener;

/**
 * Factory that creates Os cache bean.
 */
public class OsCacheFactory
    implements FactoryBean
{

  /**
   * whether to use some disk cache.
   */
  private boolean diskCache = false;

  /**
   * wheter to use some memory cache.
   */
  private boolean memoryCache = true;

  /**
   * maximal number of element to keep in cache.
   */
  private int capacity = -1;

  /**
   * the {@link FileManager} to use.
   */
  private FileManager fileManager;

  /**
   * the generated cache.
   */
  private transient Cache cache;



  /**
   * Return an instance (possibly shared or independent) of the object managed
   * by this factory.
   * <p>
   * As with a {@link  org.springframework.beans.factory.BeanFactory}, this
   * allows support for both the Singleton and Prototype design pattern.
   * <p>
   * If this FactoryBean is not fully initialized yet at the time of the call
   * (for example because it is involved in a circular reference), throw a
   * corresponding
   * {@link org.springframework.beans.factory.FactoryBeanNotInitializedException}.
   * <p>
   * As of Spring 2.0, FactoryBeans are allowed to return <code>null</code>
   * objects. The factory will consider this as normal value to be used; it will
   * not throw a FactoryBeanNotInitializedException in this case anymore.
   * FactoryBean implementations are encouraged to throw
   * FactoryBeanNotInitializedException themselves now, as appropriate.
   * 
   * @return an instance of the bean (can be <code>null</code>)
   * @throws Exception
   *           in case of creation errors
   * @see org.springframework.beans.factory.FactoryBeanNotInitializedException
   * @see org.springframework.beans.factory.FactoryBean#getObject()
   */
  public Object getObject()
      throws Exception
  {
    if (cache == null) {
      Properties properties = new Properties();
      properties.setProperty(GeneralCacheAdministrator.CACHE_CAPACITY_KEY, String.valueOf(getCapacity()));
      properties.setProperty(GeneralCacheAdministrator.CACHE_ALGORITHM_KEY, LRUCache.class.getName());
      properties.setProperty(GeneralCacheAdministrator.CACHE_MEMORY_KEY, String.valueOf(isMemoryCache()));
      properties.setProperty(GeneralCacheAdministrator.CACHE_PERSISTENCE_OVERFLOW_KEY, "true");
      if (isDiskCache()) {
        properties.setProperty(GeneralCacheAdministrator.PERSISTENCE_CLASS_KEY, HashDiskPersistenceListener.class.getName());
        properties.setProperty(HashDiskPersistenceListener.CACHE_PATH_KEY, getFileManager().createAutoDeletableTempDirectory("osCache", ".dir").getCanonicalPath());
      }
      cache = new GeneralCacheAdministrator(properties).getCache();
    }
    return cache;
  }



  /**
   * Return the type of object that this FactoryBean creates, or
   * <code>null</code> if not known in advance.
   * <p>
   * This allows one to check for specific types of beans without instantiating
   * objects, for example on autowiring.
   * <p>
   * In the case of implementations that are creating a singleton object, this
   * method should try to avoid singleton creation as far as possible; it should
   * rather estimate the type in advance. For prototypes, returning a meaningful
   * type here is advisable too.
   * <p>
   * This method can be called <i>before</i> this FactoryBean has been fully
   * initialized. It must not rely on state created during initialization; of
   * course, it can still use such state if available.
   * <p>
   * <b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
   * <code>null</code> here. Therefore it is highly recommended to implement
   * this method properly, using the current state of the FactoryBean.
   * 
   * @return the type of object that this FactoryBean creates, or
   *         <code>null</code> if not known at the time of the call
   * @see org.springframework.beans.factory.ListableBeanFactory#getBeansOfType
   * @see org.springframework.beans.factory.FactoryBean#getObjectType()
   */
  public Class<Cache> getObjectType()
  {
    return Cache.class;
  }



  /**
   * Is the object managed by this factory a singleton? That is, will
   * {@link #getObject()} always return the same object (a reference that can be
   * cached)?
   * <p>
   * <b>NOTE:</b> If a FactoryBean indicates to hold a singleton object, the
   * object returned from <code>getObject()</code> might get cached by the
   * owning BeanFactory. Hence, do not return <code>true</code> unless the
   * FactoryBean always exposes the same reference.
   * <p>
   * The singleton status of the FactoryBean itself will generally be provided
   * by the owning BeanFactory; usually, it has to be defined as singleton
   * there.
   * <p>
   * <b>NOTE:</b> This method returning <code>false</code> does not
   * necessarily indicate that returned objects are independent instances. An
   * implementation of the extended
   * {@link org.springframework.beans.factory.SmartFactoryBean} interface may
   * explicitly indicate independent instances through its
   * {@link org.springframework.beans.factory.SmartFactoryBean#isPrototype()}
   * method. Plain {@link FactoryBean} implementations which do not implement
   * this extended interface are simply assumed to always return independent
   * instances if the <code>isSingleton()</code> implementation returns
   * <code>false</code>.
   * 
   * @return whether the exposed object is a singleton
   * @see #getObject()
   * @see org.springframework.beans.factory.SmartFactoryBean#isPrototype()
   * @see org.springframework.beans.factory.FactoryBean#isSingleton()
   */
  public boolean isSingleton()
  {
    return true;
  }



  /**
   * getter for the diskCache property.
   * 
   * @return the diskCache
   */
  public boolean isDiskCache()
  {
    return diskCache;
  }



  /**
   * setter for the diskCache property.
   * 
   * @param diskCache
   *          the diskCache to set
   */
  public void setDiskCache(boolean diskCache)
  {
    this.diskCache = diskCache;
  }



  /**
   * getter for the fileManager property.
   * 
   * @return the fileManager
   */
  public FileManager getFileManager()
  {
    return fileManager;
  }



  /**
   * setter for the fileManager property.
   * 
   * @param fileManager
   *          the fileManager to set
   */
  public void setFileManager(FileManager fileManager)
  {
    this.fileManager = fileManager;
  }



  /**
   * getter for the capacity property.
   * 
   * @return the capacity
   */
  public int getCapacity()
  {
    return capacity;
  }



  /**
   * setter for the capacity property.
   * 
   * @param capacity
   *          the capacity to set
   */
  public void setCapacity(int capacity)
  {
    this.capacity = capacity;
  }



  /**
   * getter for the memoryCache property.
   * 
   * @return the memoryCache
   */
  public boolean isMemoryCache()
  {
    return memoryCache;
  }



  /**
   * setter for the memoryCache property.
   * 
   * @param memoryCache
   *          the memoryCache to set
   */
  public void setMemoryCache(boolean memoryCache)
  {
    this.memoryCache = memoryCache;
  }

}
